#pragma once
#include <iostream>
#include <string>
#include <functional>

#include <cstdlib>
#include <cstring>
#include <cerrno>

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#include "log.hpp"
#include "Protocol.hpp"


// 归类错误码
enum
{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR,
    OPENDICT_ERR,
    LISTEN_ERR,
};

namespace yzc_server
{
    using namespace std;
    static const string defaultIp = "0.0.0.0";
    static const uint16_t gport = 8080;
    static const int gbacklog = 5;

    class HttpServer
    {
        using func_t = function<void(const HttpRequest &, HttResponse &)>;

    public:
        HttpServer(const func_t &callback = {}, const uint16_t &port = gport, const string &ip = defaultIp)
            : _listensockfd(-1), _ip(ip), _port(port), _callback(callback) {}

        void initServer()
        {
            // 1.创建套接字,使用tcp协议
            _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensockfd < 0)
            {
                LogMessage(FATAL, "creat socket error");
                exit(SOCKET_ERR);
            }
            LogMessage(NORMAL, "creat listensocket success:%d", _listensockfd);

            // 2.bind绑定自己的网络信息，sockfd与IP和port
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));

            local.sin_family = PF_INET; // AF_INET就是PF_INET
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_listensockfd, (sockaddr *)&local, sizeof(local)) < 0)
            {
                LogMessage(FATAL, "server bind error");
                exit(BIND_ERR);
            }
            LogMessage(NORMAL, "server bind success");

            // 3. tcp需要建立连接！ 设置监听状态，获取新连接
            if (listen(_listensockfd, gbacklog) == -1)
            {
                LogMessage(FATAL, "server listen error");
                exit(LISTEN_ERR);
            }
            LogMessage(NORMAL, "server listen success");
        }

        void run()
        {
            // 启动服务器

            for (;;)
            {
                // tcp服务器不能直接发送消息
                // 4. tcpserver获取新连接
                struct sockaddr_in peer;
                socklen_t peerlen = sizeof(peer);
                memset(&peer, 0, sizeof(peer));

                // accept会接收新连接并会返回一个新的套接字
                // 最先创建的套接字用于listen和accept
                // 通过accept获取的套接字才可以用于我们的tcpserver和tcpclient通信
                int sockfd = accept(_listensockfd, (sockaddr *)(&peer), &peerlen); // 获取是sockfd用于通信
                if (sockfd < 0)
                {
                    // 这种虽然错误，但是不致命
                    LogMessage(ERROR, "server accept error, re accept");
                    continue; // 重新获取新连接
                }
                LogMessage(NORMAL, "server accept a new link success,get new sock:%d", sockfd);

                pid_t id = fork();
                if (id == 0)
                {
                    close(_listensockfd);
                    if (fork() > 0)
                        exit(0);
                    // 孙子进程
                    handlerHttpRequest(sockfd);
                    close(sockfd);
                    exit(0);
                }
                waitpid(id, nullptr, 0);
            }
        }
        ~HttpServer()
        {
            if (_listensockfd > 0)
                close(_listensockfd);
        }

    private:
        void handlerHttpRequest(int wrsock)
        {
            // 读取http请求
            // 反序列化http请求
            // 根据请求构建对应的响应
            // 序列化http响应
            // 发送响应send

            char buffer[4096];
            HttpRequest req;
            HttResponse resp;
            size_t n = recv(wrsock, buffer, sizeof(buffer) - 1, 0); // 大概率会读取一个完整请求
            if (n > 0)
            {
                buffer[n] = 0;
                req.inbuffer = buffer;
                req.prase();
                _callback(req, resp);
                send(wrsock, resp.outbuffer.data(), resp.outbuffer.size(), 0);
            }
        }

    private:
        int _listensockfd; // 套接字，不是用于通信的，是用来监听和获取连接
        string _ip;
        uint16_t _port;
        func_t _callback; // 回调方法
    };
}